home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1995 #5 & #6 / Amiga Plus CD - 1995 - No. 5 and 6.iso / pd / daten / ispell / source / tree.c < prev    next >
C/C++ Source or Header  |  1995-07-02  |  21KB  |  750 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: tree.c,v 1.56 1995/01/08 23:23:49 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * tree.c - a hash style dictionary for user's personal words
  8.  *
  9.  * Pace Willisson, 1983
  10.  * Hash support added by Geoff Kuenning, 1987
  11.  *
  12.  * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA
  13.  * All rights reserved.
  14.  *
  15.  * Redistribution and use in source and binary forms, with or without
  16.  * modification, are permitted provided that the following conditions
  17.  * are met:
  18.  *
  19.  * 1. Redistributions of source code must retain the above copyright
  20.  *    notice, this list of conditions and the following disclaimer.
  21.  * 2. Redistributions in binary form must reproduce the above copyright
  22.  *    notice, this list of conditions and the following disclaimer in the
  23.  *    documentation and/or other materials provided with the distribution.
  24.  * 3. All modifications to the source code must be clearly marked as
  25.  *    such.  Binary redistributions based on modified source code
  26.  *    must be clearly marked as modified versions in the documentation
  27.  *    and/or other materials provided with the distribution.
  28.  * 4. All advertising materials mentioning features or use of this software
  29.  *    must display the following acknowledgment:
  30.  *      This product includes software developed by Geoff Kuenning and
  31.  *      other unpaid contributors.
  32.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  33.  *    products derived from this software without specific prior
  34.  *    written permission.
  35.  *
  36.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  37.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  38.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  39.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  40.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  41.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  42.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  44.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  45.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46.  * SUCH DAMAGE.
  47.  */
  48.  
  49. /*
  50.  * $Log: tree.c,v $
  51.  * Revision 1.56  1995/01/08  23:23:49  geoff
  52.  * Support PDICTHOME for DOS purposes.
  53.  *
  54.  * Revision 1.55  1994/10/25  05:46:27  geoff
  55.  * Fix a comment that looked to some compilers like it might be nested.
  56.  *
  57.  * Revision 1.54  1994/01/25  07:12:15  geoff
  58.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  59.  *
  60.  */
  61.  
  62. #include <ctype.h>
  63. #include <errno.h>
  64. #include "config.h"
  65. #include "ispell.h"
  66. #include "proto.h"
  67. #include "msgs.h"
  68.  
  69. void        treeinit P ((char * p, char * LibDict));
  70. static FILE *    trydict P ((char * dictname, char * home,
  71.           char * prefix, char * suffix));
  72. static void    treeload P ((FILE * dictf));
  73. void        treeinsert P ((char * word, int wordlen, int keep));
  74. static struct dent * tinsert P ((struct dent * proto));
  75. struct dent *    treelookup P ((ichar_t * word));
  76. #if SORTPERSONAL != 0
  77. static int    pdictcmp P ((struct dent ** enta, struct dent **entb));
  78. #endif /* SORTPERSONAL != 0 */
  79. void        treeoutput P ((void));
  80. VOID *        mymalloc P ((unsigned int size));
  81. void        myfree P ((VOID * ptr));
  82. #ifdef REGEX_LOOKUP
  83. char *        do_regex_lookup P ((char * expr, int whence));
  84. #endif /* REGEX_LOOKUP */
  85.  
  86. static int        cantexpand = 0;    /* NZ if an expansion fails */
  87. static struct dent *    pershtab;    /* Aux hash table for personal dict */
  88. static int        pershsize = 0;    /* Space available in aux hash table */
  89. static int        hcount = 0;    /* Number of items in hash table */
  90.  
  91. /*
  92.  * Hash table sizes.  Prime is probably a good idea, though in truth I
  93.  * whipped the algorithm up on the spot rather than looking it up, so
  94.  * who knows what's really best?  If we overflow the table, we just
  95.  * use a double-and-add-1 algorithm.
  96.  */
  97. static int goodsizes[] =
  98.     {
  99.     53, 223, 907, 3631
  100.     };
  101.  
  102. static char        personaldict[MAXPATHLEN];
  103. static FILE *        dictf;
  104. static            newwords = 0;
  105.  
  106. void treeinit (p, LibDict)
  107.     char *        p;        /* Value specified in -p switch */
  108.     char *        LibDict;    /* Root of default dict name */
  109.     {
  110.     int            abspath;    /* NZ if p is abs path name */
  111.     char *        h;        /* Home directory name */
  112.     char        seconddict[MAXPATHLEN]; /* Name of secondary dict */
  113.     FILE *        secondf;    /* Access to second dict file */
  114.  
  115.     /*
  116.     ** If -p was not specified, try to get a default name from the
  117.     ** environment.  After this point, if p is null, the the value in
  118.     ** personaldict is the only possible name for the personal dictionary.
  119.     ** If p is non-null, then there is a possibility that we should
  120.     ** prepend HOME to get the correct dictionary name.
  121.     */
  122.     if (p == NULL)
  123.     p = getenv (PDICTVAR);
  124.     /*
  125.     ** if p exists and begins with '/' we don't really need HOME,
  126.     ** but it's not very likely that HOME isn't set anyway (on non-DOS
  127.     ** systems).
  128.     */
  129.     if ((h = getenv (HOME)) == NULL)
  130.     {
  131. #ifdef PDICTHOME
  132.     h = PDICTHOME;
  133. #else /* PDICTHOME */
  134.     return;
  135. #endif /* PDICTHOME */
  136.     }
  137.  
  138.     if (p == NULL)
  139.     {
  140.     /*
  141.      * No -p and no PDICTVAR.  We will use LibDict and DEFPAFF to
  142.      * figure out the name of the personal dictionary and where it
  143.      * is.  The rules are as follows:
  144.      *
  145.      * (1) If there is a local dictionary and a HOME dictionary,
  146.      *     both are loaded, but changes are saved in the local one.
  147.      *     The dictionary to save changes in is named "personaldict".
  148.      * (2) Dictionaries named after the affix file take precedence
  149.      *     over dictionaries with the default suffix (DEFPAFF).
  150.      * (3) Dictionaries named with the new default names
  151.      *     (DEFPDICT/DEFPAFF) take precedence over the old ones
  152.      *     (OLDPDICT/OLDPAFF).
  153.      * (4) Dictionaries aren't combined unless they follow the same
  154.      *     naming scheme.
  155.      * (5) If no dictionary can be found, a new one is created in
  156.      *     the home directory, named after DEFPDICT and the affix
  157.      *     file.
  158.      */
  159. #ifdef AMIGA    /* Personal dicts are placed with the primary dicts */
  160.     dictf = trydict (personaldict, LIBDIR, DEFPDICT, LibDict);
  161.     if (personaldict[0] == '\0')
  162.       sprintf (personaldict, "%s/%s%s", LIBDIR, DEFPDICT, LibDict);
  163. #else
  164.     dictf = trydict (personaldict, (char *) NULL, DEFPDICT, LibDict);
  165.     secondf = trydict (seconddict, h, DEFPDICT, LibDict);
  166.     if (dictf == NULL  &&  secondf == NULL)
  167.         {
  168.         dictf = trydict (personaldict, (char *) NULL, DEFPDICT, DEFPAFF);
  169.         secondf = trydict (seconddict, h, DEFPDICT, DEFPAFF);
  170.         }
  171.     if (dictf == NULL  &&  secondf == NULL)
  172.         {
  173.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, LibDict);
  174.         secondf = trydict (seconddict, h, OLDPDICT, LibDict);
  175.         }
  176.     if (dictf == NULL  &&  secondf == NULL)
  177.         {
  178.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, OLDPAFF);
  179.         secondf = trydict (seconddict, h, OLDPDICT, OLDPAFF);
  180.         }
  181.     if (personaldict[0] == '\0')
  182.         {
  183.         if (seconddict[0] != '\0')
  184.         (void) strcpy (personaldict, seconddict);
  185.         else
  186.         (void) sprintf (personaldict, "%s/%s%s", h, DEFPDICT, LibDict);
  187.         }
  188. #endif
  189.     if (dictf != NULL)
  190.         {
  191.         treeload (dictf);
  192.         (void) fclose (dictf);
  193.         }
  194. #ifndef AMIGA                                /* Only one dict */
  195.     if (secondf != NULL)
  196.         {
  197.         treeload (secondf);
  198.         (void) fclose (secondf);
  199.         }
  200. #endif
  201.     }
  202.     else
  203.     {
  204.     /*
  205.     ** Figure out if p is an absolute path name.  Note that beginning
  206.     ** with "./" and "../" is considered an absolute path, since this
  207.     ** still means we can't prepend HOME.
  208.     */
  209. #ifndef AMIGA                                /* Always absolute path on Amiga */
  210.     abspath = (*p == '/'  ||  strncmp (p, "./", 2) == 0
  211.       ||  strncmp (p, "../", 3) == 0);
  212.     if (abspath)
  213.         {
  214. #endif
  215.         (void) strcpy (personaldict, p);
  216.         if ((dictf = fopen (personaldict, "r")) != NULL)
  217.         {
  218.         treeload (dictf);
  219.         (void) fclose (dictf);
  220.         }
  221. #ifndef AMIGA
  222.         }
  223.     else
  224.         {
  225.         /*
  226.         ** The user gave us a relative pathname.  We will try it
  227.         ** locally, and if that doesn't work, we'll try the home
  228.         ** directory.  If neither exists, it will be created in
  229.         ** the home directory if words are added.
  230.         */
  231.         (void) strcpy (personaldict, p);
  232.         if ((dictf = fopen (personaldict, "r")) != NULL)
  233.         {
  234.         treeload (dictf);
  235.         (void) fclose (dictf);
  236.         }
  237.         else if (!abspath)
  238.         {
  239.         /* Try the home */
  240.         (void) sprintf (personaldict, "%s/%s", h, p);
  241.         if ((dictf = fopen (personaldict, "r")) != NULL)
  242.             {
  243.             treeload (dictf);
  244.             (void) fclose (dictf);
  245.             }
  246.         }
  247.         /*
  248.          * If dictf is null, we couldn't open the dictionary
  249.          * specified in the -p switch.  Complain.
  250.          */
  251.         if (dictf == NULL)
  252.         {
  253.         (void) fprintf (stderr, CANT_OPEN, p);
  254.         perror ("");
  255.         return;
  256.         }
  257.         }
  258. #endif
  259.     }
  260.  
  261.     if (!lflag  &&  !aflag
  262.       &&  access (personaldict, 2) < 0  &&  errno != ENOENT)
  263.     {
  264.     (void) fprintf (stderr, TREE_C_CANT_UPDATE, personaldict);
  265.     (void) sleep ((unsigned) 2);
  266.     }
  267.     }
  268.  
  269. /*
  270.  * Try to open a dictionary.  As a side effect, leaves the dictionary
  271.  * name in "filename" if one is found, and leaves a null string there
  272.  * otherwise.
  273.  */
  274. static FILE * trydict (filename, home, prefix, suffix)
  275.     char *        filename;    /* Where to store the file name */
  276.     char *        home;        /* Home directory */
  277.     char *        prefix;        /* Prefix for dictionary */
  278.     char *        suffix;        /* Suffix for dictionary */
  279.     {
  280.     FILE *        dictf;        /* Access to dictionary file */
  281.  
  282.     if (home == NULL)
  283.     (void) sprintf (filename, "%s%s", prefix, suffix);
  284.     else
  285.     (void) sprintf (filename, "%s/%s%s", home, prefix, suffix);
  286.     dictf = fopen (filename, "r");
  287.     if (dictf == NULL)
  288.     filename[0] = '\0';
  289.     return dictf;
  290.     }
  291.  
  292. static void treeload (loadfile)
  293.     register FILE *    loadfile;    /* File to load words from */
  294.     {
  295.     char        buf[BUFSIZ];    /* Buffer for reading pers dict */
  296.  
  297.     while (fgets (buf, sizeof buf, loadfile) != NULL)
  298.     treeinsert (buf, sizeof buf, 1);
  299.     newwords = 0;
  300.     }
  301.  
  302. void treeinsert (word, wordlen, keep)
  303.     char *        word;    /* Word to insert - must be canonical */
  304.     int            wordlen; /* Length of the word buffer */
  305.     int            keep;
  306.     {
  307.     register int    i;
  308.     struct dent        wordent;
  309.     register struct dent * dp;
  310.     struct dent *    olddp;
  311. #ifndef NO_CAPITALIZATION_SUPPORT
  312.     struct dent *    newdp;
  313. #endif
  314.     struct dent *    oldhtab;
  315.     int            oldhsize;
  316.     ichar_t        nword[INPUTWORDLEN + MAXAFFIXLEN];
  317. #ifndef NO_CAPITALIZATION_SUPPORT
  318.     int            isvariant;
  319. #endif
  320.  
  321.     /*
  322.      * Expand hash table when it is MAXPCT % full.
  323.      */
  324.     if (!cantexpand  &&  (hcount * 100) / MAXPCT >= pershsize)
  325.     {
  326.     oldhsize = pershsize;
  327.     oldhtab = pershtab;
  328.     for (i = 0;  i < sizeof goodsizes / sizeof (goodsizes[0]);  i++)
  329.         {
  330.         if (goodsizes[i] > pershsize)
  331.         break;
  332.         }
  333.     if (i >= sizeof goodsizes / sizeof goodsizes[0])
  334.         pershsize += pershsize + 1;
  335.     else
  336.         pershsize = goodsizes[i];
  337.     pershtab =
  338.       (struct dent *) calloc ((unsigned) pershsize, sizeof (struct dent));
  339.     if (pershtab == NULL)
  340.         {
  341.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  342.         /*
  343.          * Try to continue anyway, since our overflow
  344.          * algorithm can handle an overfull (100%+) table,
  345.          * and the malloc very likely failed because we
  346.          * already have such a huge table, so small mallocs
  347.          * for overflow entries will still work.
  348.          */
  349.         if (oldhtab == NULL)
  350.         exit (1);        /* No old table, can't go on */
  351.         (void) fprintf (stderr, TREE_C_TRY_ANYWAY);
  352.         cantexpand = 1;        /* Suppress further messages */
  353.         pershsize = oldhsize;    /* Put things back */
  354.         pershtab = oldhtab;        /* ... */
  355.         newwords = 1;        /* And pretend it worked */
  356.         }
  357.     else
  358.         {
  359.         /*
  360.          * Re-insert old entries into new table
  361.          */
  362.         for (i = 0;  i < oldhsize;  i++)
  363.         {
  364.         dp = &oldhtab[i];
  365.         if (dp->flagfield & USED)
  366.             {
  367. #ifdef NO_CAPITALIZATION_SUPPORT
  368.             (void) tinsert (dp);
  369. #else
  370.             newdp = tinsert (dp);
  371.             isvariant = (dp->flagfield & MOREVARIANTS);
  372. #endif
  373.             dp = dp->next;
  374. #ifdef NO_CAPITALIZATION_SUPPORT
  375.             while (dp != NULL)
  376.             {
  377.             (void) tinsert (dp);
  378.             olddp = dp;
  379.             dp = dp->next;
  380.             free ((char *) olddp);
  381.             }
  382. #else
  383.             while (dp != NULL)
  384.             {
  385.             if (isvariant)
  386.                 {
  387.                 isvariant = dp->flagfield & MOREVARIANTS;
  388.                 olddp = newdp->next;
  389.                 newdp->next = dp;
  390.                 newdp = dp;
  391.                 dp = dp->next;
  392.                 newdp->next = olddp;
  393.                 }
  394.             else
  395.                 {
  396.                 isvariant = dp->flagfield & MOREVARIANTS;
  397.                 newdp = tinsert (dp);
  398.                 olddp = dp;
  399.                 dp = dp->next;
  400.                 free ((char *) olddp);
  401.                 }
  402.             }
  403. #endif
  404.             }
  405.         }
  406.         if (oldhtab != NULL)
  407.         free ((char *) oldhtab);
  408.         }
  409.     }
  410.  
  411.     /*
  412.     ** We're ready to do the insertion.  Start by creating a sample
  413.     ** entry for the word.
  414.     */
  415.     if (makedent (word, wordlen, &wordent) < 0)
  416.     return;            /* Word must be too big or something */
  417.     if (keep)
  418.     wordent.flagfield |= KEEP;
  419.     /*
  420.     ** Now see if word or a variant is already in the table.  We use the
  421.     ** capitalized version so we'll find the header, if any.
  422.     **/
  423.     (void) strtoichar (nword, word, sizeof nword, 1);
  424.     upcase (nword);
  425.     if ((dp = lookup (nword, 1)) != NULL)
  426.     {
  427.     /* It exists.  Combine caps and set the keep flag. */
  428.     if (combinecaps (dp, &wordent) < 0)
  429.         {
  430.         free (wordent.word);
  431.         return;
  432.         }
  433.     }
  434.     else
  435.     {
  436.     /* It's new. Insert the word. */
  437.     dp = tinsert (&wordent);
  438. #ifndef NO_CAPITALIZATION_SUPPORT
  439.     if (captype (dp->flagfield) == FOLLOWCASE)
  440.        (void) addvheader (dp);
  441. #endif
  442.     }
  443.     newwords |= keep;
  444.     }
  445.  
  446. static struct dent * tinsert (proto)
  447.     struct dent *    proto;        /* Prototype entry to copy */
  448.     {
  449.     ichar_t        iword[INPUTWORDLEN + MAXAFFIXLEN];
  450.     register int    hcode;
  451.     register struct dent * hp;        /* Next trial entry in hash table */
  452.     register struct dent * php;        /* Prev. value of hp, for chaining */
  453.  
  454.     if (strtoichar (iword, proto->word, sizeof iword, 1))
  455.     (void) fprintf (stderr, WORD_TOO_LONG (proto->word));
  456. #ifdef NO_CAPITALIZATION_SUPPORT
  457.     upcase (iword);
  458. #endif
  459.     hcode = hash (iword, pershsize);
  460.     php = NULL;
  461.     hp = &pershtab[hcode];
  462.     if (hp->flagfield & USED)
  463.     {
  464.     while (hp != NULL)
  465.         {
  466.         php = hp;
  467.         hp = hp->next;
  468.         }
  469.     hp = (struct dent *) calloc (1, sizeof (struct dent));
  470.     if (hp == NULL)
  471.         {
  472.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  473.         exit (1);
  474.         }
  475.     }
  476.     *hp = *proto;
  477.     if (php != NULL)
  478.     php->next = hp;
  479.     hp->next = NULL;
  480.     return hp;
  481.     }
  482.  
  483. struct dent * treelookup (word)
  484.     register ichar_t *    word;
  485.     {
  486.     register int    hcode;
  487.     register struct dent * hp;
  488.     char        chword[INPUTWORDLEN + MAXAFFIXLEN];
  489.  
  490.     if (pershsize <= 0)
  491.     return NULL;
  492.     (void) ichartostr (chword, word, sizeof chword, 1);
  493.     hcode = hash (word, pershsize);
  494.     hp = &pershtab[hcode];
  495.     while (hp != NULL  &&  (hp->flagfield & USED))
  496.     {
  497.     if (strcmp (chword, hp->word) == 0)
  498.         break;
  499. #ifndef NO_CAPITALIZATION_SUPPORT
  500.     while (hp->flagfield & MOREVARIANTS)
  501.         hp = hp->next;
  502. #endif
  503.     hp = hp->next;
  504.     }
  505.     if (hp != NULL  &&  (hp->flagfield & USED))
  506.     return hp;
  507.     else
  508.     return NULL;
  509.     }
  510.  
  511. #if SORTPERSONAL != 0
  512. /* Comparison routine for sorting the personal dictionary with qsort */
  513. static int pdictcmp (enta, entb)
  514.     struct dent **    enta;
  515.     struct dent **    entb;
  516.     {
  517.  
  518.     /* The parentheses around *enta and *entb below are NECESSARY!
  519.     ** Otherwise the compiler reads it as *(enta->word), or
  520.     ** enta->word[0], which is illegal (but pcc takes it and
  521.     ** produces wrong code).
  522.     **/
  523.     return casecmp ((*enta)->word, (*entb)->word, 1);
  524.     }
  525. #endif
  526.  
  527. void treeoutput ()
  528.     {
  529.     register struct dent *    cent;    /* Current entry */
  530.     register struct dent *    lent;    /* Linked entry */
  531. #if SORTPERSONAL != 0
  532.     int                pdictsize; /* Number of entries to write */
  533.     struct dent **        sortlist; /* List of entries to be sorted */
  534.     register struct dent **    sortptr; /* Handy pointer into sortlist */
  535. #endif
  536.     register struct dent *    ehtab;    /* End of pershtab, for fast looping */
  537.  
  538.     if (newwords == 0)
  539.     return;
  540.  
  541.   printf("Saving to '%s'", personaldict);
  542.   fflush(stdout);
  543.  
  544.     if ((dictf = fopen (personaldict, "w")) == NULL)
  545.     {
  546.     (void) fprintf (stderr, CANT_CREATE, personaldict);
  547.     return;
  548.     }
  549.  
  550. #if SORTPERSONAL != 0
  551.     /*
  552.     ** If we are going to sort the personal dictionary, we must know
  553.     ** how many items are going to be sorted.
  554.     */
  555.     pdictsize = 0;
  556.     if (hcount >= SORTPERSONAL)
  557.     sortlist = NULL;
  558.     else
  559.     {
  560.     for (cent = pershtab, ehtab = pershtab + pershsize;
  561.       cent < ehtab;
  562.       cent++)
  563.         {
  564.         for (lent = cent;  lent != NULL;  lent = lent->next)
  565.         {
  566.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  567.             pdictsize++;
  568. #ifndef NO_CAPITALIZATION_SUPPORT
  569.         while (lent->flagfield & MOREVARIANTS)
  570.           lent = lent->next;
  571. #endif
  572.         }
  573.         }
  574.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  575.       cent < ehtab;
  576.       cent++)
  577.         {
  578.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  579.         {
  580.         /*
  581.         ** We only want to count variant headers
  582.         ** and standalone entries.  These happen
  583.         ** to share the characteristics in the
  584.         ** test below.  This test will appear
  585.         ** several more times in this routine.
  586.         */
  587. #ifndef NO_CAPITALIZATION_SUPPORT
  588.         if (captype (cent->flagfield) != FOLLOWCASE
  589.           &&  cent->word != NULL)
  590. #endif
  591.             pdictsize++;
  592.         }
  593.         }
  594.     sortlist = (struct dent **) malloc (pdictsize * sizeof (struct dent));
  595.     }
  596.     if (sortlist == NULL)
  597.     {
  598. #endif
  599.     for (cent = pershtab, ehtab = pershtab + pershsize;
  600.       cent < ehtab;
  601.       cent++)
  602.         {
  603.         for (lent = cent;  lent != NULL;  lent = lent->next)
  604.         {
  605.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  606.             {
  607.             toutent (dictf, lent, 1);
  608. #ifndef NO_CAPITALIZATION_SUPPORT
  609.             while (lent->flagfield & MOREVARIANTS)
  610.             lent = lent->next;
  611. #endif
  612.             }
  613.         }
  614.         }
  615.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  616.       cent < ehtab;
  617.       cent++)
  618.         {
  619.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  620.         {
  621. #ifndef NO_CAPITALIZATION_SUPPORT
  622.         if (captype (cent->flagfield) != FOLLOWCASE
  623.           &&  cent->word != NULL)
  624. #endif
  625.             toutent (dictf, cent, 1);
  626.         }
  627.         }
  628. #if SORTPERSONAL != 0
  629.     return;
  630.     }
  631.     /*
  632.     ** Produce dictionary in sorted order.  We used to do this
  633.     ** destructively, but that turns out to fail because in some modes
  634.     ** the dictionary is written more than once.  So we build an
  635.     ** auxiliary pointer table (in sortlist) and sort that.  This
  636.     ** is faster anyway, though it uses more memory. 
  637.     */
  638.     sortptr = sortlist;
  639.     for (cent = pershtab, ehtab = pershtab + pershsize;  cent < ehtab;  cent++)
  640.     {
  641.     for (lent = cent;  lent != NULL;  lent = lent->next)
  642.         {
  643.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  644.         {
  645.         *sortptr++ = lent;
  646. #ifndef NO_CAPITALIZATION_SUPPORT
  647.         while (lent->flagfield & MOREVARIANTS)
  648.             lent = lent->next;
  649. #endif
  650.         }
  651.         }
  652.     }
  653.     for (cent = hashtbl, ehtab = hashtbl + hashsize;  cent < ehtab;  cent++)
  654.     {
  655.     if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  656.         {
  657. #ifndef NO_CAPITALIZATION_SUPPORT
  658.         if (captype (cent->flagfield) != FOLLOWCASE
  659.           &&  cent->word != NULL)
  660. #endif
  661.         *sortptr++ = cent;
  662.         }
  663.     }
  664.     /* Sort the list */
  665.     qsort ((char *) sortlist, (unsigned) pdictsize,
  666.       sizeof (sortlist[0]),
  667.       (int (*) P ((const void *, const void *))) pdictcmp);
  668.     /* Write it out */
  669.     for (sortptr = sortlist;  --pdictsize >= 0;  )
  670.     toutent (dictf, *sortptr++, 1);
  671.     free ((char *) sortlist);
  672. #endif
  673.  
  674.     newwords = 0;
  675.  
  676.     (void) fclose (dictf);
  677.     }
  678.  
  679. VOID * mymalloc (size)
  680.     unsigned int    size;
  681.     {
  682.  
  683.     return malloc ((unsigned) size);
  684.     }
  685.  
  686. void myfree (ptr)
  687.     VOID * ptr;
  688.     {
  689.     if (hashstrings != NULL  &&  (char *) ptr >= hashstrings
  690.       &&  (char *) ptr <= hashstrings + hashheader.stringsize)
  691.     return;            /* Can't free stuff in hashstrings */
  692.     free (ptr);
  693.     }
  694.  
  695. #ifdef REGEX_LOOKUP
  696.  
  697. /* check the hashed dictionary for words matching the regex. return the */
  698. /* a matching string if found else return NULL */
  699. char * do_regex_lookup (expr, whence)
  700.     char *    expr;    /* regular expression to use in the match   */
  701.     int        whence;    /* 0 = start at the beg with new regx, else */
  702.             /* continue from cur point w/ old regex     */
  703.     {
  704.     static struct dent *    curent;
  705.     static int            curindex;
  706.     static struct dent *    curpersent;
  707.     static int            curpersindex;
  708.     static char *        cmp_expr;
  709.     char            dummy[INPUTWORDLEN + MAXAFFIXLEN];
  710.     ichar_t *            is;
  711.  
  712.     if (whence == 0)
  713.     {
  714.     is = strtosichar (expr, 0);
  715.     upcase (is);
  716.     expr = ichartosstr (is, 1);
  717.         cmp_expr = REGCMP (expr);
  718.         curent = hashtbl;
  719.         curindex = 0;
  720.         curpersent = pershtab;
  721.         curpersindex = 0;
  722.     }
  723.     
  724.     /* search the dictionary until the word is found or the words run out */
  725.     for (  ; curindex < hashsize;  curent++, curindex++)
  726.     {
  727.         if (curent->word != NULL
  728.           &&  REGEX (cmp_expr, curent->word, dummy) != NULL)
  729.         {
  730.         curindex++;
  731.         /* Everybody's gotta write a wierd expression once in a while! */
  732.         return curent++->word;
  733.         }
  734.     }
  735.     /* Try the personal dictionary too */
  736.     for (  ; curpersindex < pershsize;  curpersent++, curpersindex++)
  737.     {
  738.         if ((curpersent->flagfield & USED) != 0
  739.           &&  curpersent->word != NULL
  740.           &&  REGEX (cmp_expr, curpersent->word, dummy) != NULL)
  741.         {
  742.         curpersindex++;
  743.         /* Everybody's gotta write a wierd expression once in a while! */
  744.         return curpersent++->word;
  745.         }
  746.     }
  747.     return NULL;
  748.     }
  749. #endif /* REGEX_LOOKUP */
  750.